//---------------[CSE420]----------[Project #4]----------[Part #2]---------------
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <libspe2.h>
#include <cbe_mfc.h>
#include <pthread.h>
#include <unistd.h>
#include <math.h>
#include "common.h"


//------------------------------[Data Structures]--------------------------------
/* Data structure for running SPE thread */
typedef struct ppu_pthread_data
{
	spe_context_ptr_t   speid;
	pthread_t           pthread;
	uint64_t           *argp;
} ppu_pthread_data_t;


//------------------------------[Global Variables]-------------------------------
int matrixA_InPPE[NUM_OF_ROWS][NUM_OF_COLS]  QWORD_ALIGNED;
int matrixB_InPPE[NUM_OF_ROWS][NUM_OF_COLS]  QWORD_ALIGNED;
int matrixC_InPPE[NUM_OF_ROWS][NUM_OF_COLS]  QWORD_ALIGNED;
extern spe_program_handle_t spu;
CONTROL_BLOCK cb QWORD_ALIGNED;
ppu_pthread_data_t data;


//----------------------------[Forward Declarations]-----------------------------
void *ppu_pthread_function(void *arg);
void initMatrices ();
void printMatrices();


//------------------------------------[Main]-------------------------------------
int main(int argc, char *argv[])
{
	int rc;
	initMatrices();

	/* Fill in control block */
	cb.dataA = &matrixA_InPPE[0][0];
	cb.dataB = &matrixB_InPPE[0][0];
	cb.dataC = &matrixC_InPPE[0][0];

	/* Create SPE context and load SPE program into the SPE context */
	if ((data.speid = spe_context_create (0, NULL)) == NULL)
	{
		fprintf(stderr, "Failed spe_context_create(errno=%d strerror=%s)\n", errno, strerror(errno));
		exit (3);
	}
  
	/* Load program */
	if ((rc = spe_program_load (data.speid, &spu)) != 0)
	{
		fprintf (stderr, "Failed spe_program_load(errno=%d strerror=%s)\n", errno, strerror(errno));
		exit (3);
    }
  

	/* Pass Control Block address to SPU */
	data.argp = (uint64_t *) &cb;

/********************************************************/
#ifdef TIMING
long unsigned int start_t, end_t;
start_t = __mftb();
#endif
/********************************************************/

	/* Create SPE pthreads */
	if ((rc = pthread_create (&data.pthread, NULL, &ppu_pthread_function, &data)) != 0)
    {
		fprintf (stderr, "Failed pthread_create(errno=%d strerror=%s)\n", errno, strerror(errno));
		exit (3);
    }      
  
	/* Wait for SPE to complete */
	if ((rc = pthread_join (data.pthread, NULL)) != 0)
	{
		fprintf (stderr, "Failed pthread_join(rc=%d, errno=%d strerror=%s)\n", rc, errno, strerror(errno));
		exit (1);
	}


/********************************************************/
#ifdef TIMING
end_t = __mftb();
printf("\n\n\nTotal time used by SPE: %f us\n\n\n", (float)((end_t-start_t)/79.8));
#endif
/********************************************************/
  
	/* Destroy the SPE contexts */
	if ((rc = spe_context_destroy (data.speid)) != 0)
    {
		fprintf (stderr, "Failed spe_context_destroy(rc=%d, errno=%d strerror=%s)\n", rc, errno, strerror(errno));
		exit (1);
    }

	printMatrices();
	return 0;
}


//---------------------------------[Functions]-----------------------------------
/* Create and run one SPE thread */
void *ppu_pthread_function(void *arg)
{
	ppu_pthread_data_t *datap = (ppu_pthread_data_t *)arg;
	int rc;
	unsigned int entry = SPE_DEFAULT_ENTRY;
	if ((rc = spe_context_run(datap->speid, &entry, 0, datap->argp, NULL, NULL)) < 0)
	{
		fprintf(stderr, "Failed spe_context_run(rc=%d, errno=%d, strerror=%s)\n", rc, errno, strerror(errno));
		exit(1);
	}
	pthread_exit(NULL);
}

/* Initializes A and B with random integers from (0, 100] */
void initMatrices()
{
	for(int row = 0; row < NUM_OF_ROWS; row++)
	{
		for(int col = 0; col < NUM_OF_COLS; col++)
		{
			matrixA_InPPE[row][col] = random() % 100;
			matrixB_InPPE[row][col] = random() % 100;
		}
	}

}

/* Prints the A and B input matrices, and the resulting C matrix */
void printMatrices()
{
	int row, col;

	printf("Matrix A:\n");
	for(row = 0; row < NUM_OF_ROWS; row++)
	{
		for(col = 0; col < NUM_OF_COLS; col++)
		{
			printf("%8d", matrixA_InPPE[row][col]);
		}
		printf("\n");
	}

	printf("Matrix B:\n");
	for(row = 0; row < NUM_OF_ROWS; row++)
	{
		for(col = 0; col < NUM_OF_COLS; col++)
		{
			printf("%8d", matrixB_InPPE[row][col]);
		}
		printf("\n");
	}

	printf("Matrix C:\n");
	for(row = 0; row < NUM_OF_ROWS; row++)
	{
		for(col = 0; col < NUM_OF_COLS; col++)
		{
			printf("%8d", matrixC_InPPE[row][col]);
		}
		printf("\n");
	}
}
//------------------------------------[END]--------------------------------------
